home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 7 / Apprentice-Release7.iso / Source Code / C ++ / Applications / NeuroSim 1.0.2 / AGA Slider ƒ / Sources / CAGASlider.cp < prev    next >
Encoding:
Text File  |  1997-03-21  |  25.6 KB  |  922 lines  |  [TEXT/CWIE]

  1. //
  2. //    CAGASlider.cp
  3. //
  4. //    Apple Grayscale Appearance Slider class for PowerPlant
  5. //    Subclassed from LControl
  6. //
  7. //    version 1.4.2 - March 21, 1997
  8. //
  9. //    Copyright © 1996-1997 by James Jennings. All rights reserved.
  10. //
  11. /*
  12.     Optimization notes: I basically did three things (in slider v1.3.1)
  13.         1. Calculated the smallest rectangle to redraw while tracking the mouse.
  14.                 (stored in mChangedRect)
  15.         2. Don't draw tick marks while tracking the mouse.
  16.                 (This assumes the tick marks won't change while tracking.)
  17.         3. Precalculated the value returned by GetTrackRect() and cached it. 
  18.                 (stored in mTrackRect)
  19.     
  20.     The StOffscreenGWorld constructor calls EraseRect() unnecessarily 
  21.     since I later erase everything to a light gray.
  22.     I can't fix this without rewriting StOffscreenGWorld and have declined to 
  23.     do this at present. This could be done later if necessary.
  24. */
  25.  
  26. //    A thought: 
  27. // We can replace mIsTracking with IsTracking() { return GetValue() != mTrackingValue; }
  28.  
  29. // Uncomment the next line to turn on profiling for drawing and tracking.
  30. //#include <profiler.h>
  31.  
  32. #include "CAGASlider.h"
  33. #include <LStream.h>
  34. #include <UDrawingUtils.h>
  35. #include <UDrawingState.h>
  36. #include <PP_Messages.h>
  37. #include <UGWorld.h>
  38.  
  39. // Note that the flag AGA_VERSION won't change the indicator/thumb. 
  40. // You have to replace the PICT 1000 resource for that.
  41. //#define AGA_VERSION 1    // Apple's specs as of April 96
  42. #define AGA_VERSION 2    // Apple's specs as of Sept 96
  43.  
  44. // constants that control the geometry of the slider
  45. static const Int16 kEndMargin = 10;        // where the indicator stops.
  46. static const Int16 kFlatMargin = 4;        // margin on the flat side on an indicator
  47. static const Int16 kPointMargin = 6;    // margin on the pointy side--not including tickmarks
  48. static const Int16 kTrackWidth = 5;        // width of the indicator's track
  49. static const Int16 kOffsideCutoff = 24;    // abort by dragging this far from control
  50. static const Int16 kTrackToTick = 10;    // center of track to start of tick mark
  51. static const Int16 kTickLen = 6;        // length of tick mark
  52. static const Int16 kIndSize = 16;        // size of indicator image
  53. static const Int16 kDepthCutoff = 4;// 4 or 8. The screen depth to start grayscale drawing.
  54.  
  55. // If there are no tick marks, the slider is 16 pixels wide.
  56. // If there are tick marks, the slider is approximately...
  57. //const Int16 kSliderWidth = kFlatMargin+kTrackWidth+kTrackToTick+kTickLen; // = 25 pixels
  58.  
  59. // Class variables for holding the indicator image data.
  60. LGWorld    *    CAGASlider::sColorData = 0;
  61. LGWorld    *    CAGASlider::sBlackAndWhiteData = 0;
  62. LGWorld    *    CAGASlider::sMaskData = 0;
  63.  
  64. // Indicator image related constants.
  65. const Rect         kPICTDataSize = { 0, 0, 6*kIndSize, 4*kIndSize };
  66. const ResIDT    k8BitPICTid = 1000;
  67. const ResIDT    k1BitPICTid = 1001;
  68. const ResIDT    kMaskPICTid = 1002;
  69.  
  70. CAGASlider*    CAGASlider::CreateFromStream(LStream *inStream)
  71. {
  72.     return new CAGASlider( inStream );
  73. }
  74.  
  75. CAGASlider::CAGASlider(LStream *inStream)
  76.     : LControl( inStream ), mState(ind_Enabled), mContinuousBroadcast(true),
  77.       mIsTracking(false), mTrackingValue(0), mMinIsBottomOrRight(false)
  78. {
  79.     GetColor( ga_Background, mBackground );
  80.     
  81.     Int32 directionCode;
  82.     inStream->ReadBlock( &directionCode, sizeof(directionCode) );
  83.     
  84.     // Convert the entered code to the enumerated type
  85.     switch ( directionCode ) {
  86.     case 'hori':    mDirection = ind_Horizontal;    break;
  87.     case 'vert':    mDirection = ind_Vertical;        break;
  88.     case 'nort':    mDirection = ind_North;            break;
  89.     case 'sout':    mDirection = ind_South;            break;
  90.     case 'east':    mDirection = ind_East;            break;
  91.     case 'west':    mDirection = ind_West;            break;
  92.     default:    // anything else (like 'prop'), base on the proportions of the pane
  93.         SDimension16 theSize;
  94.         GetFrameSize( theSize );
  95.         if ( theSize.width > theSize.height ) mDirection = ind_Horizontal;
  96.         else mDirection = ind_Vertical;
  97.         break;
  98.     }
  99.     
  100.     // If mNumberOfTicks = 0, don't display any tick marks
  101.     // If mNumberOfTicks = 1, display one tick mark per value
  102.     // If mNumberOfTicks > 1, then mNumberOfTicks is the number of tickmarks (intervals + 1)
  103.     inStream->ReadBlock( &mNumberOfTicks, sizeof(mNumberOfTicks) );
  104.     
  105.     // mContinuousBroadcast == true -> broadcast change everytime the indicator moves
  106.     // mContinuousBroadcast == false -> wait for mouseUp
  107.     inStream->ReadBlock( &mContinuousBroadcast, sizeof(mContinuousBroadcast) );
  108.     
  109.     // mMinIsBottomOrRight == false -> mValue increases as you slide down or right
  110.     //        Behaves like a standard scrollbar. 
  111.     // mMinIsBottomOrRight == true -> mValue increases as you slide up or left
  112.     //        Is more intuitive for vertical sliders.
  113.     inStream->ReadBlock( &mMinIsBottomOrRight, sizeof(mMinIsBottomOrRight) );
  114.     
  115.     // do we need this line?
  116. //    mState = ( (mEnabled != triState_Off) ? ind_Enabled : ind_Disabled);
  117.     
  118.     CalcTrackRect();
  119.     CalcLocalFrameRect(mChangedRect);    // reasonable default value
  120. }
  121.  
  122. CAGASlider::CAGASlider(const CAGASlider &inSlider)    // copy constructor
  123.     : LControl( inSlider ), mState( ind_Enabled ), mIsTracking( false ), mTrackingValue( 0 ), 
  124.       mDirection( inSlider.mDirection ), 
  125.       mNumberOfTicks( inSlider.mNumberOfTicks ),
  126.       mContinuousBroadcast( inSlider.mContinuousBroadcast ), 
  127.       mMinIsBottomOrRight( inSlider.mMinIsBottomOrRight )
  128. {
  129.     CalcTrackRect();
  130.     CalcLocalFrameRect(mChangedRect);    // reasonable default value
  131. }
  132.  
  133. CAGASlider::~CAGASlider()
  134. {
  135. }
  136.  
  137. void CAGASlider::Purge()
  138. {    // Release the memory containing the indicator images. (Not directly used by the class.)
  139.     // Call it when memory is low, or when a window full of sliders is closed.
  140.     // It doesn't hurt to call this too often, since the sliders will reallocate 
  141.     // the images if necessary. 
  142.     // 
  143.     // If you don't want to be aggressive about reclaiming memory, 
  144.     // you don't have to call this at all. The images only take a few K.
  145.     
  146.     if ( sColorData != nil ) {
  147.         delete sColorData;
  148.         sColorData = nil;
  149.     }
  150.     
  151.     if ( sBlackAndWhiteData != nil ) {
  152.         delete sBlackAndWhiteData;
  153.         sBlackAndWhiteData = nil;
  154.     }
  155.     
  156.     if ( sMaskData != nil ) {
  157.         delete sMaskData;
  158.         sMaskData = nil;
  159.     }
  160. }
  161.  
  162. void CAGASlider::DrawSelf(void)
  163. {
  164. #ifdef __PROFILER__
  165.     Boolean profilerIsOn = ProfilerGetStatus();
  166.     ProfilerSetStatus(true);    // turn on the profiler
  167. #endif
  168.     Rect theFrame, r;
  169.     if (mIsTracking) {
  170.         // optimize by only drawing what changes (new in slider v 1.3.1
  171.         theFrame = mChangedRect;
  172.     } else {
  173.         CalcLocalFrameRect( theFrame );
  174.     }
  175.     
  176.     StDeviceLoop devices(theFrame);
  177.     Int16 depth;
  178.  
  179.     while (devices.NextDepth(depth)) {
  180.     
  181.         Boolean useGrays = (depth >= kDepthCutoff);
  182.         
  183.         // find the overlap between the frame and the current device
  184.         r = (*devices.GetCurrentDevice())->gdRect;
  185.         ::GlobalToLocal(&topLeft(r));
  186.         ::GlobalToLocal(&botRight(r));
  187.         if ( !::SectRect( &theFrame, &r, &r ) ) continue;
  188.         
  189.         // Detect the background color so we may fill the offscreen world correctly.
  190.         if ( useGrays )
  191.             DetectBackgroundColor();
  192.     //    RGBColor theBackground;
  193.     //    if (useGrays)
  194.     //        GetBackground( theBackground );
  195.             
  196.         // Use an offscreen world to avoid flickering
  197.         StOffscreenGWorld offscreen( r, depth );
  198.         
  199.         DrawBackground( r, useGrays );
  200.     /*    if (useGrays) {
  201.             // fill in the offscreen world with the background color
  202.             ::RGBBackColor( &theBackground );
  203.             ::EraseRect( &r );
  204.         }
  205.     */    
  206.         DrawSelfBasic(useGrays);
  207.             
  208.     }
  209. #ifdef __PROFILER__
  210.     ProfilerSetStatus(profilerIsOn);    // turn off the profiler
  211. #endif
  212. }
  213.  
  214. void CAGASlider::DrawSelfBasic( Boolean useGrays )
  215. {
  216. //    Rect theFrame;
  217. //    CalcLocalFrameRect( theFrame );
  218.     StColorPenState::Normalize();
  219.     
  220.     EIndicatorState theState = ( IsEnabled() ? mState : ind_Disabled );
  221.     
  222.     DrawTrack( theState, useGrays );
  223.     
  224.     if ( !mIsTracking )    // don't need ticks while tracking indicator
  225.         DrawTickMarks( mNumberOfTicks, theState, useGrays );
  226.     
  227.     if ( mIsTracking )    // Draw ghost indicator
  228.         DrawIndicator( mTrackingValue, ind_Ghost, useGrays );
  229.     
  230.     // Draw indicator *over* the ghost if they overlap.
  231.     DrawIndicator( GetValue(), theState, useGrays );
  232. }
  233.  
  234. void CAGASlider::DrawTrack( EIndicatorState inState, Boolean inUseGrays )
  235. {
  236.     StColorPenState::Normalize();
  237.     
  238.     Rect r;
  239.     GetTrackRect( r );
  240.     
  241.     const Int16 kRoundness = 4;
  242.     
  243.     // Colors depend on inState
  244.     EGAColor black, gray;
  245.     if ( inState == ind_Disabled ) {
  246.         gray = ga_4;
  247.         black = ga_8;
  248.     } else {
  249.         gray = ga_5;
  250.         black = ga_Black;
  251.     }
  252.  
  253.     if ( inUseGrays ) {
  254. #if (AGA_VERSION==2)
  255.         if (inState != ind_Disabled) {
  256. #endif
  257.             // Draw white and gray hilites
  258.             r.bottom++;
  259.             r.right++;
  260.             SetForeColor( ga_White );
  261.             ::PaintRoundRect( &r, kRoundness, kRoundness );
  262.             
  263.             ::OffsetRect( &r, -1, -1 );
  264.             SetForeColor( gray );
  265.             ::PaintRoundRect( &r, kRoundness, kRoundness );
  266.             
  267.             r.top++;
  268.             r.left++;
  269. #if (AGA_VERSION==2)
  270.         } else {
  271.         
  272.             // A disabled track still needs a fill color
  273.             SetForeColor( gray );
  274.             ::PaintRoundRect( &r, kRoundness, kRoundness );
  275.             
  276.         }
  277. #endif
  278.         SetForeColor( black );
  279.     } else {
  280.         if (inState == ind_Disabled) 
  281.             ::PenPat(&UQDGlobals::GetQDGlobals()->gray);
  282.     }
  283.     ::FrameRoundRect( &r, kRoundness, kRoundness );
  284. }
  285.  
  286. void CAGASlider::DrawIndicator( Int32 inValue, EIndicatorState inState, Boolean inUseGrays )
  287. {
  288.     // where to draw it
  289.     Rect r;
  290.     ValueToIndicatorRect( inValue, r );
  291.     
  292.     //Commented out old version of the code: It didn't work in all cases.
  293.     //ResIDT    icon = ind_First_ics8_ID + inDirection * ind_StateOffset + inState;
  294.     //DrawIcon1( r, icon, inUseGrays ); break;
  295.     
  296.     DrawIcon( r, mDirection, inState, inUseGrays );
  297. }
  298.  
  299. /*
  300.     DrawIcon1() was my prefered method of drawing the indicator.
  301.     It did not work on all monitors on all machines.
  302.     It also failed under QuickDrawGX.
  303.     Is there a bug in the PPC version of the Toolbox code?
  304.     
  305. void    CAGASlider::DrawIcon1( Rect inWhere, ResIDT inWhich, Boolean inUseGrays )
  306. {
  307.     OSErr anErr;
  308.     // PlotIconID() needs a 16x16 rectangle.
  309.     inWhere.bottom = inWhere.top + kIndSize;
  310.     inWhere.right = inWhere.left + kIndSize;
  311.     
  312.     anErr = ::PlotIconID( &inWhere, atNone, ttNone, inWhich );
  313.     ThrowIfOSErr_( anErr );
  314. }
  315. */
  316.  
  317. void    CAGASlider::DrawIcon( Rect inWhere, EIndicatorDirection inDirection, 
  318.                             EIndicatorState inState, Boolean inUseGrays )
  319. {    // draw the indicator out of a PICT
  320.     
  321.     // PlotIconID() needs a 16x16 rectangle.
  322.     inWhere.bottom = inWhere.top + kIndSize;
  323.     inWhere.right = inWhere.left + kIndSize;
  324.  
  325.     // we initialize the data we need (only does something on the first call)
  326.     if ( inUseGrays ) InitColorData();
  327.     else InitBlackAndWhiteData();
  328.     
  329.     GWorldPtr imageWorld = (inUseGrays ? sColorData : sBlackAndWhiteData )->GetMacGWorld();
  330.     GWorldPtr maskWorld = sMaskData->GetMacGWorld();
  331.     Assert_( imageWorld != nil && maskWorld != nil );
  332.     
  333.     PixMapHandle image = ::GetGWorldPixMap( imageWorld );
  334.     PixMapHandle mask  = ::GetGWorldPixMap( maskWorld );
  335.     ThrowIfNil_( image );
  336.     ThrowIfNil_( mask );
  337.     
  338.     Boolean notPurged;
  339.     notPurged = ::LockPixels( image );
  340.     notPurged = ::LockPixels( mask );
  341.     
  342.     Rect r = {0,0,kIndSize,kIndSize};
  343.     Rect maskRect = r;
  344.     ::OffsetRect( &r, inState * kIndSize, inDirection * kIndSize );
  345.     ::OffsetRect( &maskRect, 0, inDirection * kIndSize );
  346.     
  347.     GrafPtr thePort;
  348.     ::GetPort( & thePort );
  349.     
  350.     ::ForeColor( blackColor );
  351.     ::BackColor( whiteColor );
  352.     //    ::CopyMask( srcBits, maskBits, dstBits, srcRect, maskRect, dstRect );
  353.     ::CopyMask( (BitMapPtr)*image, (BitMapPtr)*mask, &(thePort->portBits), 
  354.                     &r, &maskRect, &inWhere );
  355.     
  356.     ::UnlockPixels( image );
  357.     ::UnlockPixels( mask );
  358. }
  359.  
  360. void    CAGASlider::InitColorData()
  361. {
  362.     InitMaskData();
  363.     
  364.     if ( sColorData != nil ) return;
  365.     
  366.     sColorData = new LGWorld( kPICTDataSize, 8 );
  367.     ThrowIfNil_( sColorData );
  368.     
  369.     InitPICTDataHelper( sColorData, k8BitPICTid, kPICTDataSize );
  370. }
  371.  
  372. void    CAGASlider::InitBlackAndWhiteData()
  373. {
  374.     InitMaskData();
  375.     
  376.     if ( sBlackAndWhiteData != nil ) return;
  377.     
  378.     sBlackAndWhiteData = new LGWorld( kPICTDataSize, 1 );
  379.     ThrowIfNil_( sBlackAndWhiteData );
  380.     
  381.     InitPICTDataHelper( sBlackAndWhiteData, k1BitPICTid, kPICTDataSize );
  382. }
  383.  
  384. void    CAGASlider::InitMaskData()
  385. {
  386.     if ( sMaskData != nil ) return;
  387.     
  388.     Rect r = kPICTDataSize;
  389.     r.right = r.left + kIndSize;
  390.     sMaskData = new LGWorld( r, 1 );
  391.     ThrowIfNil_( sMaskData );
  392.     
  393.     InitPICTDataHelper( sMaskData, kMaskPICTid, r );
  394. }
  395.  
  396. void    CAGASlider::InitPICTDataHelper( LGWorld *inWorld, const ResIDT inPICT, const Rect inRect )
  397. {
  398.     PicHandle aPicH = ::GetPicture( inPICT );
  399.     ThrowIfNil_( aPicH );
  400.     
  401.     inWorld->BeginDrawing();
  402.     ::DrawPicture( aPicH, &inRect );
  403.     inWorld->EndDrawing();
  404.     
  405.     ::ReleaseResource( (Handle)aPicH );
  406. }
  407.  
  408. void    CAGASlider::DetectBackgroundColor()
  409. {
  410.     // The Problem:
  411.     // Alas, the slider will not always be drawn on a ga_2 background.
  412.     // The "Front Tab" pane has a ga_1 background.
  413.     // To solve this problem, I will attempt to detect the background color
  414.     // that is presumably drawn by the enclosing view.
  415.     
  416.     Rect trackRect;
  417.     GetTrackRect( trackRect );
  418.     
  419.     // There should be a background pixel just beyond the top left corner of the trackRect
  420.     // either when the slider is first drawn, or when tracking the mouse.
  421.     ::GetCPixel( trackRect.left - 1, trackRect.top - 1, &mBackground);
  422. }
  423.  
  424. void    CAGASlider::DrawBackground( const Rect &inRect, Boolean inUseGrays )
  425. {
  426.     // Fill in the offscreen world with the background color.
  427.     // Override this method if you have a non-solid background. (eg: a pattern)
  428.     if ( inUseGrays ) {
  429.         ::RGBBackColor( &mBackground );
  430.         ::EraseRect( &inRect );
  431.     }
  432. }
  433.  
  434. void    CAGASlider::DrawTickMarks( UInt16 inNumber, EIndicatorState inState, Boolean inUseGrays )
  435. {
  436.     if ( inNumber == 0 ) return;
  437.     if ( inNumber == 1 ) inNumber = mMaxValue - mMinValue + 1;
  438.     if ( inNumber <= 1 ) return;
  439.     // Only "pointy" indicators have tickmarks.
  440.     if ( mDirection == ind_Horizontal || mDirection == ind_Vertical ) return;
  441.     
  442.     Rect theTrack;
  443.     GetTrackRect( theTrack );
  444.     
  445.     // Calculate the first tick mark rect, and the last tick mark position
  446.     Rect firstTick;
  447.     Point p = ValueToPosition( mMinValue );    // pos of 1st tick
  448.     Point q = ValueToPosition( mMaxValue );    // pos of last tick
  449.     switch ( mDirection ) {    // SetRect( r, left, top, right, bottom ), SetPt( p, h, v )
  450.     case ind_North:
  451.         p.v -= kTrackToTick + kTickLen + 1;
  452.         q.v -= kTrackToTick + kTickLen + 1;
  453.         ::SetRect( &firstTick, p.h, p.v, p.h + 1, p.v + kTickLen );
  454.         break;
  455.     case ind_South:
  456.         p.v += kTrackToTick;
  457.         q.v += kTrackToTick;
  458.         ::SetRect( &firstTick, p.h, p.v, p.h + 1, p.v + kTickLen );
  459.         break;
  460.     case ind_East:
  461.         p.h += kTrackToTick;
  462.         q.h += kTrackToTick;
  463.         ::SetRect( &firstTick, p.h, p.v, p.h + kTickLen, p.v + 1 );
  464.         break;
  465.     case ind_West:
  466.         p.h -= kTrackToTick + kTickLen + 1;
  467.         q.h -= kTrackToTick + kTickLen + 1;
  468.         ::SetRect( &firstTick, p.h, p.v, p.h + kTickLen, p.v + 1 );
  469.         break;
  470.     case ind_Horizontal:
  471.     case ind_Vertical:
  472.     default:
  473.         SignalPStr_("\pBad Slider Class Member");
  474.     }
  475.     
  476.     Int32 intervals = inNumber - 1;
  477.     Assert_( intervals > 0 );
  478.     for( Int32 i = 0; i <= intervals; i++ ) {
  479.     
  480.         Int16 deltah = (q.h - p.h) * i / intervals;
  481.         Int16 deltav = (q.v - p.v) * i / intervals;
  482.         Rect r = firstTick;
  483.         ::OffsetRect( &r, deltah, deltav );
  484.         
  485.         if ( inUseGrays ) {
  486.         
  487.             // Colors depend on inState
  488.             EGAColor black, gray;
  489.             if ( inState == ind_Disabled ) {
  490.                 gray = ga_4;
  491.                 black = ga_8;
  492.             } else {
  493.                 gray = ga_7;
  494.                 black = ga_Black;
  495.             }
  496.             
  497.             PaintRect( r, black );
  498.  
  499. #if AGA_VERSION==2
  500.             if ( inState != ind_Disabled ) {
  501.                 ::InsetRect( &r, -1, -1 );
  502.                 UGrayscaleAppearance::FrameRect( r, ga_White, gray, ga_Background );
  503.             }
  504. #else
  505.             ::InsetRect( &r, -1, -1 );
  506.             UGrayscaleAppearance::FrameRect( r, ga_White, gray, ga_Background );
  507. #endif        
  508.             
  509.         } else {
  510.         
  511.             if (inState == ind_Disabled) 
  512.                 ::PenPat(&UQDGlobals::GetQDGlobals()->gray);
  513.             ::PaintRect( &r );
  514.             
  515.         }
  516.     }
  517. }
  518.  
  519. CAGASlider::EOrientation CAGASlider::GetOrientation(void)
  520. {
  521.     EOrientation theResult;
  522.     switch ( mDirection ) {
  523.     case ind_Horizontal:
  524.     case ind_North:
  525.     case ind_South:
  526.         theResult = orient_Horizontal;
  527.         break;
  528.     case ind_Vertical:
  529.     case ind_East:
  530.     case ind_West:
  531.         theResult = orient_Vertical;
  532.         break;
  533.     default:
  534.         SignalPStr_("\pBad Slider Class Member");
  535.     }
  536.     return theResult;
  537. }
  538.  
  539. void CAGASlider::ValueToIndicatorRect( Int32 inValue, Rect &outRect )
  540. {
  541.     Point center = ValueToPosition( inValue );
  542.     
  543.     switch ( mDirection ) {
  544.     case ind_Horizontal:
  545.         //    SetRect() arguments are: Rect*, left, top, right, bottom
  546.         ::SetRect( &outRect, center.h - 6, center.v - 8, center.h + 7, center.v + 8 );
  547.         break;
  548.     case ind_North:
  549.         ::SetRect( &outRect, center.h - 7, center.v -10, center.h + 8, center.v + 6 );
  550.         break;
  551.     case ind_South:
  552.         ::SetRect( &outRect, center.h - 7, center.v - 7, center.h + 8, center.v + 9 );
  553.         break;
  554.     case ind_Vertical:
  555.         ::SetRect( &outRect, center.h - 8, center.v - 6, center.h + 8, center.v + 7 );
  556.         break;
  557.     case ind_East:
  558.         ::SetRect( &outRect, center.h - 7, center.v - 7, center.h + 9, center.v + 8 );
  559.         break;
  560.     case ind_West:
  561.         ::SetRect( &outRect, center.h -10, center.v - 7, center.h + 6, center.v + 8 );
  562.         break;
  563.     default:
  564.         SignalPStr_("\pBad Slider Class Member");
  565.     }
  566. }
  567.  
  568. Point    CAGASlider::ValueToPosition( Int32 inValue )
  569. {    // find the position of the center of the indicator that corresponds to inValue
  570.     Point p;    // the result
  571.     
  572.     Assert_( mMinValue <= inValue && inValue <= mMaxValue );
  573.     
  574.     Int32 valueRange = mMaxValue - mMinValue;
  575.     Int16 pixelRange;
  576.     
  577.     // Handle special case to avoid a divide by zero later.
  578.     if ( valueRange <= 0 ) {
  579.         inValue = mMinValue;
  580.         valueRange = 1;
  581.     }
  582.     
  583.     // Flip the direction of increasing value, if asked.
  584.     Int32 startValue;
  585.     if ( mMinIsBottomOrRight ) {
  586.         startValue = mMaxValue;
  587.         valueRange = - valueRange;
  588.     } else {
  589.         startValue = mMinValue;
  590.     }
  591.     
  592.     Rect theTrack;
  593.     GetTrackRect( theTrack );
  594.     
  595.     // Non-pointy indicators (which have no tick marks) aren't as 
  596.     // wide, so we adjust the placement.
  597.     Int16 margin = kEndMargin;
  598.     if ( mDirection == ind_Horizontal || mDirection == ind_Vertical ) margin--;
  599.     
  600.     switch( GetOrientation() ) {
  601.     case orient_Horizontal:
  602.         theTrack.right--;    // aesthetic adjustments
  603.         theTrack.bottom++;
  604.         pixelRange = theTrack.right - theTrack.left - 2*margin;
  605.         p.h = theTrack.left + margin + 
  606.                 pixelRange * (inValue - startValue) / valueRange;
  607.         p.v = ( theTrack.top + theTrack.bottom )/2;
  608.         break;
  609.     case orient_Vertical:
  610.         theTrack.bottom--;    // aesthetic adjustments
  611.         theTrack.right++;
  612.         pixelRange = theTrack.bottom - theTrack.top - 2*margin;
  613.         p.v = theTrack.top + margin + 
  614.                 pixelRange * (inValue - startValue) / valueRange;
  615.         p.h = ( theTrack.right + theTrack.left )/2;
  616.         break;
  617.     default:
  618.         SignalPStr_("\pBad Slider Orientation");
  619.         break;
  620.     }
  621.     
  622.     return p;
  623. }
  624.  
  625. Int32    CAGASlider::PositionToValue( Point inPosition )
  626. {
  627.     // Handle special case to avoid a divide by zero later.
  628.     if ( mMaxValue <= mMinValue ) return mMinValue;
  629.     
  630.     Rect theTrack;
  631.     GetTrackRect( theTrack );
  632.     
  633.     // If we move the mouse too far away from the slider, 
  634.     // return the slider's old value (thus aborting the slide).
  635.     Point offside = {0,0};    // number of pixels off to the side of the slider
  636.     
  637.     if ( inPosition.v < theTrack.top )        offside.v = theTrack.top - inPosition.v;
  638.     if ( inPosition.v > theTrack.bottom )    offside.v = inPosition.v - theTrack.bottom;
  639.     if ( inPosition.h < theTrack.left )        offside.h = theTrack.left - inPosition.h;
  640.     if ( inPosition.h > theTrack.right )    offside.h = inPosition.h - theTrack.right;
  641.     
  642.     if ( (offside.h > kOffsideCutoff) || (offside.v > kOffsideCutoff) )
  643.         return GetValue();    // we're "offside" -- return the slider's current value
  644.     
  645.     // Calculate the value.
  646.     
  647.     Int32 offset, range, result;
  648.     
  649.     switch( GetOrientation() ) {
  650.     case orient_Horizontal:
  651.         offset = inPosition.h - theTrack.left - kEndMargin;
  652.         range = theTrack.right - theTrack.left - 2*kEndMargin;
  653.         Assert_( range > 0 );
  654.         break;
  655.     case orient_Vertical:
  656.         offset = inPosition.v - theTrack.top - kEndMargin;
  657.         range = theTrack.bottom - theTrack.top - 2*kEndMargin;
  658.         Assert_( range > 0 );
  659.         break;
  660.     default:
  661.         SignalPStr_("\pBad Slider Orientation");
  662.         break;
  663.     }
  664.     
  665.     // Flip the direction of increasing value, if asked.
  666.     if ( mMinIsBottomOrRight )
  667.         offset = range - offset;
  668.     
  669.     // Adjust the offset to center the indicator-value on a tick mark instead of between them.
  670.     offset += range / ( mMaxValue - mMinValue ) / 2;
  671.     
  672.     // calculate result
  673.     result = mMinValue + ( mMaxValue - mMinValue ) * offset / range;
  674.     
  675.     // range clipping
  676.     if ( result < mMinValue ) result = mMinValue;
  677.     if ( result > mMaxValue ) result = mMaxValue;
  678.     
  679.     return result;
  680. }
  681.  
  682. void    CAGASlider::ResizeFrameBy(Int16 inWidthDelta, Int16 inHeightDelta, Boolean inRefresh)
  683. {
  684.     LControl::ResizeFrameBy( inWidthDelta, inHeightDelta, inRefresh);
  685.     CalcTrackRect();
  686. }
  687.  
  688. void    CAGASlider::MoveBy(Int32 inHorizDelta, Int32 inVertDelta, Boolean inRefresh)
  689. {
  690.     LControl::MoveBy( inHorizDelta, inVertDelta, inRefresh);
  691.     CalcTrackRect();
  692. }
  693.  
  694. void    CAGASlider::GetTrackRect( Rect &outRect )
  695. {
  696.     // Optimize by caching the track rectangle. (New in slider v1.3.1)
  697.     outRect = mTrackRect;
  698. }
  699.  
  700. void    CAGASlider::CalcTrackRect()
  701. {    // Calculate the rectange to draw the track into.
  702.     //
  703.     // This is the rect of the small black RoundRect. The white and gray highlights
  704.     // are outside this rect. Try to base all drawing on this rect.
  705.     //
  706.     // The compass direction sliders are pushed to the side of the pane away from the point.
  707.     // The plain sliders are centered.
  708.     
  709.     CalcLocalFrameRect( mTrackRect );
  710.     
  711.     SDimension16 theSize;
  712.     GetFrameSize( theSize );
  713.  
  714.     // Center the track in the frame
  715.     switch ( GetOrientation() ) {
  716.     
  717.     case orient_Horizontal:
  718.         
  719.         switch ( mDirection ) {
  720.         case ind_Horizontal:
  721.             mTrackRect.top += (theSize.height - kTrackWidth)/2;
  722.             break;
  723.         case ind_North:
  724.             mTrackRect.top = mTrackRect.bottom - kFlatMargin - kTrackWidth;
  725.             break;
  726.         case ind_South:
  727.             mTrackRect.top += kFlatMargin;
  728.             break;
  729.         }
  730.         mTrackRect.bottom = mTrackRect.top + kTrackWidth;
  731.         mTrackRect.left++;
  732.         mTrackRect.right--;
  733.         break;
  734.         
  735.     case orient_Vertical:
  736.     
  737.         switch ( mDirection ) {
  738.         case ind_Vertical:
  739.             mTrackRect.left += (theSize.width - kTrackWidth)/2;
  740.             break;
  741.         case ind_East:
  742.             mTrackRect.left += kFlatMargin;
  743.             break;
  744.         case ind_West:
  745.             mTrackRect.left = mTrackRect.right - kFlatMargin - kTrackWidth;
  746.             break;
  747.         }
  748.         mTrackRect.right = mTrackRect.left + kTrackWidth;
  749.         mTrackRect.top++;
  750.         mTrackRect.bottom--;
  751.         break;
  752.     }
  753. }
  754.  
  755. Int16    CAGASlider::FindHotSpot(Point inPoint)    // Rewrite w/o icons !!!
  756. {
  757.     // default is None.
  758.     Int16 result = hot_None;
  759.     
  760.     // Is it in the track?
  761.     Rect r;
  762.     GetTrackRect( r );
  763.     ::InsetRect( &r, -2, -2 );
  764.     
  765.     if ( ::PtInRect( inPoint, &r ) ) result = hot_Track;
  766.     
  767.     // Is it in the indicator? If so, override track.
  768.     ValueToIndicatorRect( GetValue(), r );
  769.     
  770.     // code which relys on the PICT mask
  771.     if ( ::PtInRect( inPoint, &r ) ) {
  772.     
  773.         Point p;
  774.         p.h = inPoint.h - r.left;
  775.         p.v = inPoint.v - r.top + mDirection * kIndSize;
  776.         
  777.         // test the value of the pixel in the mask PICT (is there a toolbox call for this?)
  778.         InitMaskData();
  779.         PixMapHandle pm = ::GetGWorldPixMap( sMaskData->GetMacGWorld() );
  780.         Assert_( pm != nil );
  781.         
  782.         Ptr aPtr = ::GetPixBaseAddr( pm );
  783.         Int16 rowBytes = (*pm)->rowBytes & 0x3FFF;
  784.         aPtr += rowBytes * p.v + (p.h/8);
  785.         
  786.         if ( *aPtr & (1<<(7 - (p.h%8))) ) 
  787.             result = hot_Indicator;
  788.     }
  789.     
  790.     return result;
  791. }
  792.  
  793. Boolean    CAGASlider::PointInHotSpot(Point inPoint, Int16 inHotSpot)
  794. {
  795.     if ( inHotSpot == 0 ) return false;
  796.     
  797.     return ( FindHotSpot( inPoint ) == inHotSpot );
  798. }
  799.  
  800. void    CAGASlider::HotSpotAction(Int16 inHotSpot, Boolean inCurrInside, Boolean inPrevInside)
  801. {    // Not used? Keep it for SimulateHotSpotClick().
  802.     if ( inHotSpot != 1 ) return;    // sanity check
  803.     
  804.     if ( inCurrInside != inPrevInside ) {
  805.         if ( inCurrInside ) mState = ind_Pressed;
  806.         else mState = ind_Enabled;
  807.         Draw( nil );
  808.     }
  809. }
  810.  
  811. void    CAGASlider::HotSpotResult(Int16 inHotSpot)
  812. {
  813.     if (inHotSpot != 1) return;
  814.     
  815.     // done dragging, un-press the indicator
  816.     mState = ind_Enabled;
  817.     Draw( nil );
  818. }
  819.  
  820. Boolean    CAGASlider::TrackHotSpot(Int16 inHotSpot, Point inPoint)
  821. {
  822. #ifndef Debug_Signal
  823.     // inHotSpot is only used in the Assert_(), and therefore only if Debug_Signal is defined.
  824.     #pragma unused(inHotSpot)
  825. #endif
  826.     Assert_ ( inHotSpot == hot_Indicator || inHotSpot == hot_Track );
  827.     
  828. #ifdef __PROFILER__
  829.     Boolean profilerIsOn = ProfilerGetStatus();
  830.     ProfilerSetStatus(true);    // turn on the profiler
  831. #endif
  832.  
  833.     mIsTracking = true;        // should this be a "St" object?
  834.     mTrackingValue = GetValue();
  835.     
  836.     Rect    oldRect, newRect;
  837.     ValueToIndicatorRect(mTrackingValue, newRect);
  838.     
  839.     // "Press" the indicator
  840.     mState = ind_Pressed;    // should this be a "St" object?
  841.     mChangedRect = newRect;
  842.     Draw( nil );
  843.     
  844.     // Track the mouse while it is down
  845.     Point    currPt = inPoint;
  846.     while (StillDown()) {
  847.     
  848.         ::GetMouse(&currPt);
  849.         
  850.         // calculate ghost position
  851.         Int32 currValue = PositionToValue( currPt );
  852.         
  853.         if ( currValue != mTrackingValue ) {
  854.         
  855.             mTrackingValue = currValue;
  856.             
  857.             if ( mContinuousBroadcast ) {
  858.             
  859.                 // this code modified from LControl::BroadcastValueMessage()
  860.                 if (mValueMessage != msg_Nothing) {
  861.                     Int32    value = mTrackingValue;    // broadcast tracking value
  862.                     BroadcastMessage(mValueMessage, (void*) &value);
  863.                 }
  864.                 
  865.             }
  866.             
  867.             // Optimize by remembering the rect that has changed. (new in slider v 1.3.1)
  868.             oldRect = newRect;
  869.             ValueToIndicatorRect(mTrackingValue, newRect);
  870.             ::UnionRect( &oldRect, &newRect, &mChangedRect );
  871.             
  872.             // Note: Draw() must come after BroadcastMessage() since some LListener
  873.             // might do some drawing of it's own and change the drawing focus.
  874.             // We could also just call FocusDraw(), but this saves a few cycles.
  875.             Draw( nil );
  876.         }
  877.     }
  878.     
  879.     mState = ind_Enabled;
  880.                                     // Check if MouseUp occurred in HotSpot
  881.     EventRecord    macEvent;            // Get location from MouseUp event
  882.     if (::GetOSEvent(mUpMask, &macEvent)) {
  883.         currPt = macEvent.where;
  884.         ::GlobalToLocal(&currPt);
  885.         mTrackingValue = PositionToValue( currPt );
  886.     }
  887.     
  888.     mIsTracking = false;    // must be reset before the final Draw()
  889.  
  890.     SetValue( mTrackingValue );    // SetValue() calls BroadcastValueMessage()
  891. #ifndef SLIDER_DRAWS_SELF
  892.     Draw( nil );
  893. #endif // SLIDER_DRAWS_SELF
  894.     
  895. #ifdef __PROFILER__
  896.     ProfilerSetStatus(profilerIsOn);    // turn off the profiler
  897. #endif
  898.     
  899.     return true;    // tell caller that mouse was released in hot-spot
  900. }
  901.  
  902. #ifdef SLIDER_DRAWS_SELF
  903. void    CAGASlider::SetValue( Int32 inValue )
  904. {    // If the value changes, pass to superclass and redraw.
  905.     
  906.     if ( inValue != GetValue() ) {
  907.         LControl::SetValue( inValue );
  908.         Draw(nil);
  909.     }
  910. }
  911.  
  912. void    CAGASlider::EnableSelf()
  913. {
  914.     Refresh(); 
  915. }
  916.  
  917. void    CAGASlider::DisableSelf()
  918. {
  919.     Refresh(); 
  920. }
  921. #endif // SLIDER_DRAWS_SELF
  922.